package com.am.server.common.base.advice;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import com.am.server.common.base.exception.BusinessException;
import com.am.server.common.base.exception.NoTokenException;
import com.am.server.common.base.model.dto.ResponseMessageDto;
import com.am.server.common.constant.MessageConstant;
import com.am.server.common.util.ResponseUtils;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.Objects;

/**
 * 通用校验放回校验
 *
 * @author 阮雪峰
 */
@Slf4j
@RestControllerAdvice
public class ControllerAdvice {

    /**
     * form表单验证错误
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<ResponseMessageDto> formBindException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        log.error("表单验证错误, 参数：{}", bindingResult.getFieldErrors());
        e.printStackTrace();
        return ResponseUtils.badRequest(Objects.requireNonNull(bindingResult.getFieldError()).toString());
    }

    /**
     * raw格式json数据校验
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ResponseMessageDto> rawBindException(MethodArgumentNotValidException e) {
        log.error("raw格式json数据校验, 参数：{}", e.getBindingResult().getFieldErrors());
        e.printStackTrace();
        BindingResult bindingResult = e.getBindingResult();
        return ResponseUtils.badRequest(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
    }

    /**
     * 空结果集异常
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(EmptyResultDataAccessException.class)
    public ResponseEntity<ResponseMessageDto> emptyResultDataAccessException(EmptyResultDataAccessException e) {
        log.error(e.toString(), e);
        return ResponseUtils.badRequest(e.getMessage());
    }

    /**
     * 参数转换异常
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<ResponseMessageDto> methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        String methodName = Objects.requireNonNull(e.getParameter().getMethod()).getName();
        String parameterName = e.getName();
        String parameterType = e.getParameter().getParameterType().getSimpleName();
        String actualParameterType = Objects.requireNonNull(e.getValue()).getClass().getSimpleName();
        String actualParameterValue = Objects.requireNonNull(e.getValue()).toString();

        log.error("方法：{}，参数：{}，类型为：{}，实际类型为：{}，参数值：{}",
                methodName,
                parameterName,
                parameterType,
                actualParameterType,
                actualParameterValue);

        return ResponseUtils.badRequest(MessageConstant.PARAMETER_TYPE_ERROR);
    }

    /**
     * 参数转换异常
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(InvalidFormatException.class)
    public ResponseEntity<ResponseMessageDto> invalidFormatException(InvalidFormatException e) {
        String parameterName = e.getPathReference();
        String parameterType = e.getTargetType().getSimpleName();
        String actualParameterType = Objects.requireNonNull(e.getValue()).getClass().getSimpleName();
        String actualParameterValue = Objects.requireNonNull(e.getValue()).toString();
        log.error("参数：{}，类型为：{}，实际类型为：{}，参数值：{}",
                parameterName,
                parameterType,
                actualParameterType,
                actualParameterValue);

        return ResponseUtils.badRequest(MessageConstant.PARAMETER_TYPE_ERROR);
    }

    /**
     * 请求没有携带token，提示登录，401
     *
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(NoTokenException.class)
    public ResponseEntity<ResponseMessageDto> noTokenException() {
        log.error("token，提示登录，401");
        return ResponseUtils.unauthorized(MessageConstant.NO_TOKEN);

    }

    /**
     * token过期或者不是正确的token，412
     *
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(NotLoginException.class)
    public ResponseEntity<ResponseMessageDto> tokenException(NotLoginException exception) {
        exception.printStackTrace();
        log.error("token过期或者不是正确的token，412");
        return ResponseUtils.preconditionFailed(MessageConstant.TOKEN_EXPIRED);

    }

    /**
     * 没有权限访问，403
     *
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(NotPermissionException.class)
    public ResponseEntity<ResponseMessageDto> noPermissionException(NotPermissionException e) {
        log.error("没有权限访问，403", e);
        return ResponseUtils.forbidden(MessageConstant.NO_PERMISSION_ACCESS);

    }

    /**
     * @param e HttpRequestMethodNotSupportedException
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<ResponseMessageDto> httpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e) {
        log.error("请求方法不支持，支持方法：{}，请求方法：{}", e.getSupportedHttpMethods(), e.getMethod());
        e.printStackTrace();
        return ResponseUtils.serverError(String.format("请求方法不支持，支持方法：%s，请求方法：%s", e.getSupportedHttpMethods(), e.getMethod()));
    }

    /**
     * 业务异常
     *
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ResponseMessageDto> businessError(BusinessException e) {
        log.error("业务异常", e);
        return ResponseUtils.extendResponse(e.getCode(), e.getMessage());
    }

    /**
     * 服务器异常
     *
     * @return ResponseEntity<ResponseMessageDto>
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ResponseMessageDto> serverError(RuntimeException e) {
        log.error("服务异常", e);
        return ResponseUtils.serverError(MessageConstant.SERVER_ERROR);
    }
}
